package ui;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.MatteBorder;

import logic.DisplayPackage;
import logic.InputDecoder;
import net.miginfocom.swing.MigLayout;
import sharedUtils.Constants;
import storage.Task;
import storage.TaskList;


public class Gui implements Runnable {
	//@author A0101655N
	protected static final String COMMAND_HELP = "help";
	protected static final String COMMAND_EXIT = "exit";
	protected static final String COMMAND_QUIT = "quit";
	protected static final String COMMAND_DISPLAY = "display";
	
	protected static final String MESSAGE_INVALID_COMMAND = "Invalid command.";
	protected static final String MESSAGE_ADD_SUCCESSFUL = "You have added:";
	protected static final String MESSAGE_DELETE_SUCCESSFUL = "You have deleted:";
	protected static final String MESSAGE_SEARCH_SUCCESSFUL = "You have searched:";
	protected static final String MESSAGE_SEARCH_NOT_FOUND = "No match for:";
	protected static final String MESSAGE_EDIT_SUCCESSFUL = "You have edited:";
	protected static final String MESSAGE_UNDO_SUCCESSFUL = "Undo is completed.";
	protected static final String MESSAGE_REDO_SUCCESSFUL = "Redo is completed.";
	protected static final String MESSAGE_ADD_UNSUCCESSFUL = "Task addition is NOT completed.";
	protected static final String MESSAGE_DELETE_UNSUCCESSFUL = "Task deletion is NOT completed.";
	protected static final String MESSAGE_SEARCH_UNSUCCESSFUL = "Searching is NOT completed.";
	protected static final String MESSAGE_EDIT_UNSUCCESSFUL = "Editing is NOT completed.";
	protected static final String MESSAGE_UNDO_UNSUCCESSFUL = "Undo is NOT completed.";
	protected static final String MESSAGE_REDO_UNSUCCESSFUL = "Redo is NOT completed.";
	protected static final String MESSAGE_ERROR_NO_TITLE = "Please enter a title for your task.";
	
	//@author A0097758E
	private static final String DATE_DELIMITER = "-";
	private static final String TIME_DELIMITER = ":";
	//private static final String GAP_BETWEEN_VALUES = "	";

	private static final Color PROGRAM_BG_COLOR = new Color(205, 219, 232);
	private static final Color RESULTTEXTPANEL_BG_COLOR = new Color(205, 219, 232);
	
	//@author A0097005J
	private static final Color TASK_COLOR_1 = new Color(204, 229 , 255);
	private static final Color TASK_COLOR_2 = new Color(153, 204 , 255);
	private static final Color TASK_COLOR_1_MOUSEOVER = new Color(214, 239 , 255);
	private static final Color TASK_COLOR_2_MOUSEOVER = new Color(163, 214 , 255);
	private static final Color TASK_COLOR_1_HIGH = new Color(255, 204 , 204);
	private static final Color TASK_COLOR_2_HIGH = new Color(255, 153 , 153);
	private static final Color TASK_COLOR_1_HIGH_MOUSEOVER = new Color(255, 214 , 214);
	private static final Color TASK_COLOR_2_HIGH_MOUSEOVER = new Color(255, 163 , 163);
	private static final Color TASK_COLOR_1_EXPIRED = new Color(160, 160 , 160);
	private static final Color TASK_COLOR_1_EXPIRED_MOUSEOVER = new Color(170, 170 , 170);
	private static final Color TASK_COLOR_2_EXPIRED = new Color(128, 128 , 128);
	private static final Color TASK_COLOR_2_EXPIRED_MOUSEOVER = new Color(138, 138 , 138);
	private static final Color[] COLORLIST = {TASK_COLOR_1, TASK_COLOR_2, TASK_COLOR_1_HIGH, TASK_COLOR_2_HIGH, TASK_COLOR_1_EXPIRED, TASK_COLOR_2_EXPIRED};
	private static final int NUM_OF_COLORS = 2;
	private static final int TASK_COLOR_HIGH_POS = 2;
	private static final int TASK_COLOR_EXPIRED_POS = 4;
	
	//@author A0097758E
	/**
	 * HTML Font Formatting Strings
	 */
	private static final int STATUS_PANE = 1;
	private static final int HELP_PANE = 2;
	
	private static final String STATUS_OPEN_TAGS = "<div style=\""
			+ "font:Calibri;"
			+ "font-size: 12px;"
			+ "font-weight: bold;"
			+ "color: gray;"
			+ "text-indent: 2px;"
			+ " \">";
	private static final String STATUS_CLOSE_TAGS = "</div>";
	private static final String HELP_OPEN_TAGS = "<div style=\""
			+ "font:Calibri;"
			+ "font-size: 9px;"
			+ " \">";
	private static final String HELP_CLOSE_TAGS = "</div>";
	private static final String TASKPANE_OPEN_TAGS = "<div style=\""
			+ "font:Calibri;"
			+ "color: black;"
			+ " \">";
	private static final String TASKPANE_CLOSE_TAGS = "</div>";
	private static final String TITLE_OPEN_TAGS = "<div style=\""
			+ "font-size: 14px;"
			+ "font-weight: bold;"
			+ " \">";
	private static final String TITLE_CLOSE_TAGS = "</div>";
	private static final String DATE_OPEN_TAGS = "<div style=\""
			+ "font-size: 9px;"
			+ " \">";
	private static final String DATE_CLOSE_TAGS = "</div>";
	private static final String DESC_OPEN_TAGS = "<div style=\""
			+ "font-size: 9px;"
			+ " \"><i>";
	private static final String DESC_CLOSE_TAGS = "</i></div>";
	
	//@author A0097005J
	private static final Dimension SCREEN_SIZE = Toolkit.getDefaultToolkit().getScreenSize();
	private static final int DIMX = SCREEN_SIZE.width/4;
	private static final int DIMY = DIMX;
	private static final int POSX = SCREEN_SIZE.width/4;
	private static final int POSY = SCREEN_SIZE.height/4;
	
	/*private static final int programWidth = 300;
	private static final int programHeight = 480;*/
	
	private static final Dimension TASK_PREFERRED_SIZE = new Dimension(100, 110);
	/*private static final Dimension TASK_MIN_SIZE = new Dimension(programWidth, 200);
	private static final Dimension TASK_MAX_SIZE = new Dimension(programWidth, Integer.MAX_VALUE);
	private static final Dimension HELP_DISPLAY_SIZE = new Dimension(200, 100);
	private static final Dimension WINDOW_SIZE = new Dimension(DIMX, DIMY);*/
	
	
	//@author ide
	private JFrame frmQuicktodoBeta;
	private TaskList taskList;
	private JTextField textInput;
	private JTextPane resultTextPanel;
	private JScrollPane scrollPane;
	private JPanel taskDisplayPane;
	
	private int curColor = 0;
	
	//@author A0097005J
	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {		
		Gui gui = new Gui();		
		gui.run();
	}
	
	//@author A0097758E
	@Override
	public void run() {
		try {
			if(taskList == null) {
				exit();
			} else {
				this.frmQuicktodoBeta.setVisible(true);
				
				resultTextPanel.setText(applyFontTreatment(
						STATUS_PANE, processInput(COMMAND_DISPLAY)));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Create the application.
	 */
	public Gui() {
		initialize();
		this.taskList = new TaskList();
	}

	public Gui(TaskList taskList) {
		initialize();
		this.taskList = taskList;
	}
	
	private String applyFontTreatment(int panelId, String str) {
		if(str == null || panelId < 0) {
			return null;
		}
		
		switch(panelId) {
			case STATUS_PANE: 
				return applyForStatus(str);
			case HELP_PANE:
				return applyForHelp(str);
		}
		
		return str;
	}

	private String applyForStatus(String str) {
		StringBuilder sb = new StringBuilder();
		
		sb.append(STATUS_OPEN_TAGS);
		sb.append(str);
		sb.append(STATUS_CLOSE_TAGS);
		
		return sb.toString();
	}
	
	private String applyForHelp(String str) {
		StringBuilder sb = new StringBuilder();
		
		sb.append(HELP_OPEN_TAGS);
		sb.append(str);
		sb.append(HELP_CLOSE_TAGS);
		
		return sb.toString();
	}

	//@author A0101655N
	/**
	 * Process the user input, and pass it to InputDecoder (if the input is not exit/quit/help),
	 * then receive a DisplayPackage object, check the command code, and display relevant messages and output
	 * 
	 * @param input the string inputed by user
	 * 
	 * @return string of feedback messages or the tasks if the user input is "display",
	 * or null if input is null
	 */
	private String processInput(String input) {

		if (input == null) {
			return null;
		} else {
			String outputLine = "";

			if (input.equals(COMMAND_EXIT) || input.equals(COMMAND_QUIT)) {
				exit();
			} else if (input.equals(COMMAND_HELP)) {
				displayQuickHelp();
			} else {
				InputDecoder id = new InputDecoder();
				DisplayPackage returnedObject;

				returnedObject = id.exec_command(input, taskList);

				switch (returnedObject.getCommandCode()) {
					case Constants.INVALID_CODE : {
						return MESSAGE_INVALID_COMMAND;
					}
					case Constants.ADD_CODE : {
						return MESSAGE_ADD_SUCCESSFUL + "\n" + displayTaskInPackage(returnedObject);
					}
					case Constants.DELETE_CODE : {
						return MESSAGE_DELETE_SUCCESSFUL + "\n" + displayTaskInPackage(returnedObject);
					}
					case Constants.SEARCH_CODE : {
						return displayAllTaskInPackage(returnedObject);
					}
					case Constants.EDIT_CODE : {
						return MESSAGE_EDIT_SUCCESSFUL + "\n" + displayTaskInPackage(returnedObject);
					}
					case Constants.UNDO_CODE : {
						return MESSAGE_UNDO_SUCCESSFUL;
					}
					case Constants.REDO_CODE : {
						return MESSAGE_REDO_SUCCESSFUL;
					}
					case Constants.DISPLAY_CODE : {					
						return displayAllTaskInPackage(returnedObject);
					}
					case Constants.ADD_INVALID_CODE : {
						return MESSAGE_ADD_UNSUCCESSFUL;
					}
					case Constants.DELETE_INVALID_CODE : {
						return MESSAGE_DELETE_UNSUCCESSFUL;
					}
					case Constants.SEARCH_INVALID_CODE : {
						return MESSAGE_SEARCH_UNSUCCESSFUL;
					}
					case Constants.EDIT_INVALID_CODE : {
						return MESSAGE_EDIT_UNSUCCESSFUL;
					}
					case Constants.UNDO_INVALID_CODE : {
						return MESSAGE_UNDO_UNSUCCESSFUL;
					}
					case Constants.REDO_INVALID_CODE : {
						return MESSAGE_REDO_UNSUCCESSFUL;
					}
				}
			}
			return outputLine;
		}
	}
	
	//@author A0097005J
	private void displayQuickHelp() {
		
		taskDisplayPane.removeAll();
		
		final JTextPane textPane = new JTextPane();
		textPane.setBackground(COLORLIST[0]);
		
		textPane.setPreferredSize(null);
		textPane.setMinimumSize(null);
		textPane.setContentType("text/html");
		textPane.setEditable(false);
		
		String displayString = getHelpString();
		textPane.setText(applyFontTreatment(HELP_PANE, displayString));
		textPane.setName("Help");

		//Highlights task on mouseover
		textPane.addMouseListener(new MouseAdapter() {
			
			@Override
			public void mouseEntered(MouseEvent arg0) {
				if(textPane.getBackground().equals(TASK_COLOR_1)) {
					textPane.setBackground(TASK_COLOR_1_MOUSEOVER);
				}
			}

			@Override
			public void mouseExited(MouseEvent arg0) {
				if(textPane.getBackground().equals(TASK_COLOR_1_MOUSEOVER)) {
					textPane.setBackground(TASK_COLOR_1);
				}
			}
		});
		
		taskDisplayPane.add(textPane);
		scrollPane.validate();
		
	}
	
	//@author A0101655N
	private String displayTaskInPackage(DisplayPackage pack) {
		if(pack == null) {
			return "";
		}
		Task task = pack.getTaskData();
		if(task == null) {
			return "There is not task(s) to display."; //failed
		}
		
		taskDisplayPane.removeAll();
		printTask(task);
		return "";
	}
	
	private String displayAllTaskInPackage(DisplayPackage pack) {
		if(pack == null) {
			return "";
		}
		ArrayList<Task> allTask = pack.getList();
		if(allTask == null) {
			return "There is not task(s) to display."; //failed
		}
		
		taskDisplayPane.removeAll();
		printListOfTask(allTask);
		return "You have " + allTask.size() + " task(s) in total.";
	}
	
	//@author A0097005J
	private void printTask(Task task) {
		createPaneToDisplayTask(task);
		scrollPane.validate();
	}
	
	private void printListOfTask(ArrayList<Task> listOfTask) {
		for(int i=0; i<listOfTask.size(); i++) {
			createPaneToDisplayTask(listOfTask.get(i));
		}
		
		if(listOfTask.size() == 0) {
			initTaskDisplayPane();
		}
		scrollPane.validate();
		
	}
	
	private void createPaneToDisplayTask(Task task) {
		//GregorianCalendar currTime = new GregorianCalendar();
		final JTextPane textPane = new JTextPane();
		
		if(task.getEndDate() != null && task.getEndDate().getTimeInMillis() < System.currentTimeMillis()) {
			curColor = (curColor+1)%NUM_OF_COLORS+TASK_COLOR_EXPIRED_POS;
			textPane.setBackground(COLORLIST[curColor]);
		} else if(task.getPriority() == Constants.LOW_PRIORITY) {
			curColor = (curColor+1)%NUM_OF_COLORS;
			textPane.setBackground(COLORLIST[curColor]);
		} else if (task.getPriority() == Constants.HIGH_PRIORITY) {
			curColor = (curColor+1)%NUM_OF_COLORS+TASK_COLOR_HIGH_POS;
			textPane.setBackground(COLORLIST[curColor]);
		}
		
		textPane.setPreferredSize(TASK_PREFERRED_SIZE);
		//textPane.setMinimumSize(TASK_MIN_SIZE);
		//textPane.setMaximumSize(TASK_MAX_SIZE);
		textPane.setContentType("text/html");
		textPane.setEditable(false);
		textPane.setToolTipText(task.getDescription());
		
		String displayString = formatTask(task);
		textPane.setText(TASKPANE_OPEN_TAGS + displayString + TASKPANE_CLOSE_TAGS);
		textPane.setName(Integer.toString(task.getId()));
		
		//Highlights task on mouseover
		textPane.addMouseListener(new MouseAdapter() {			
			@Override
			public void mouseEntered(MouseEvent arg0) {
				if(textPane.getBackground().equals(TASK_COLOR_1)) {
					textPane.setBackground(TASK_COLOR_1_MOUSEOVER);
				} else if(textPane.getBackground().equals(TASK_COLOR_2)) {
					textPane.setBackground(TASK_COLOR_2_MOUSEOVER);
				} else if(textPane.getBackground().equals(TASK_COLOR_1_HIGH)) {
					textPane.setBackground(TASK_COLOR_1_HIGH_MOUSEOVER);
				} else if(textPane.getBackground().equals(TASK_COLOR_2_HIGH)) {
					textPane.setBackground(TASK_COLOR_2_HIGH_MOUSEOVER);
				} else if(textPane.getBackground().equals(TASK_COLOR_1_EXPIRED)) {
					textPane.setBackground(TASK_COLOR_1_EXPIRED_MOUSEOVER);
				} else if(textPane.getBackground().equals(TASK_COLOR_2_EXPIRED)) {
					textPane.setBackground(TASK_COLOR_2_EXPIRED_MOUSEOVER);
				}
			}

			@Override
			public void mouseExited(MouseEvent arg0) {
				if(textPane.getBackground().equals(TASK_COLOR_1_MOUSEOVER)) {
					textPane.setBackground(TASK_COLOR_1);
				} else if(textPane.getBackground().equals(TASK_COLOR_2_MOUSEOVER)) {
					textPane.setBackground(TASK_COLOR_2);
				} else if(textPane.getBackground().equals(TASK_COLOR_1_HIGH_MOUSEOVER)) {
					textPane.setBackground(TASK_COLOR_1_HIGH);
				} else if(textPane.getBackground().equals(TASK_COLOR_2_HIGH_MOUSEOVER)) {
					textPane.setBackground(TASK_COLOR_2_HIGH);
				} else if(textPane.getBackground().equals(TASK_COLOR_1_EXPIRED_MOUSEOVER)) {
					textPane.setBackground(TASK_COLOR_1_EXPIRED);
				} else if(textPane.getBackground().equals(TASK_COLOR_2_EXPIRED_MOUSEOVER)) {
					textPane.setBackground(TASK_COLOR_2_EXPIRED);
				}
			}
		});
		
		taskDisplayPane.add(textPane);		
	}
	
	//@author A0097758E
	private String formatTask(Task task) {
		if(task == null) {
			return null;
		}
		
		StringBuilder sb = new StringBuilder();
		String sDate, sTime, eDate, eTime;
		
		sDate = "";
		sTime = "";
		eDate = "";
		eTime = "";
		
		if(task.isStartDateSet()) {
			sDate = formatDate(task.getStartDate());
		}
		
		if(task.isStartTimeSet()) {			
			sTime = formatTime(task.getStartDate());
		}
		
		if(task.isEndDateSet()) {
			eDate = formatDate(task.getEndDate());
		}
		if(task.isEndTimeSet()) {
			eTime = formatTime(task.getEndDate());
		}
		
		sb.append(TITLE_OPEN_TAGS);
		sb.append(task.getTitle());
		sb.append(" (Task Id: "+ Integer.toString(task.getId()) +")");
		sb.append(TITLE_CLOSE_TAGS);
		
		if(task.isStartDateSet() || task.isStartTimeSet()) {
			sb.append(DATE_OPEN_TAGS);
			sb.append("Start Date/Time: ");
			
			if(task.isStartDateSet()) {
				sb.append(sDate);
			}
			
			if(task.isStartDateSet() && task.isStartTimeSet()) {
				sb.append(", ");
			}
			
			if(task.isStartTimeSet()) {
				sb.append(sTime);
			}
			
			sb.append(DATE_CLOSE_TAGS);
		}
		
		if(task.isEndDateSet() || task.isEndTimeSet()) {
			sb.append(DATE_OPEN_TAGS);
			sb.append("End Date/Time: &nbsp;");
			
			if(task.isEndDateSet()) {
				sb.append(eDate);
			}
			
			if(task.isEndDateSet() && task.isEndTimeSet()) {
				sb.append(", ");
			}
			
			if(task.isEndTimeSet()) {
				sb.append(eTime);
			}
			sb.append(DATE_CLOSE_TAGS);		
		}
		
		if(task.getDescription() != null) {
			sb.append(DESC_OPEN_TAGS);
			sb.append(task.getDescription());
			sb.append(DESC_CLOSE_TAGS);
		}
		
		return sb.toString();
		
	}
	
	private String formatDate(GregorianCalendar gcal) {
		return String.format("%02d", (gcal.get(Calendar.DAY_OF_MONTH))) + 
				DATE_DELIMITER + String.format("%02d", (gcal.get(Calendar.MONTH) + 1)) + 
				DATE_DELIMITER + String.format("%04d", (gcal.get(Calendar.YEAR)));
	}
	
	private String formatTime(GregorianCalendar gcal) {
		return String.format("%02d", (gcal.get(Calendar.HOUR_OF_DAY))) + 
				TIME_DELIMITER + String.format("%02d", (gcal.get(Calendar.MINUTE)));
	}

	//@author A0097005J
	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		initProgramOverallPanel();
		initTextInput();
		initResultTextPanel();
		initScrollPane();
		initTaskDisplayPane();

		textInput.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					String input = textInput.getText();
					String output = processInput(input);
					
					resultTextPanel.setText(
							applyFontTreatment(STATUS_PANE, output));
					textInput.setText("");
				}
			}
		});

	}

	private void initProgramOverallPanel() {
		frmQuicktodoBeta = new JFrame();
		frmQuicktodoBeta.setBackground(PROGRAM_BG_COLOR);
		frmQuicktodoBeta.getContentPane().setBackground(PROGRAM_BG_COLOR);
		frmQuicktodoBeta.setIconImage(Toolkit.getDefaultToolkit().getImage(Gui.class.getResource("/resource/QUICK-TODO-ICON.png")));
		frmQuicktodoBeta.setResizable(false);
		//frame.setSize(WINDOW_SIZE);
		frmQuicktodoBeta.setBounds(POSX, POSY, DIMX, DIMY);
		frmQuicktodoBeta.setMinimumSize(new Dimension(320, 480));
		frmQuicktodoBeta.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frmQuicktodoBeta.getContentPane().setLayout(
				new MigLayout("", "[247.00,grow]", "[][21px:21px][grow]"));
		frmQuicktodoBeta.setTitle("QuickTODO");
	}

	private void initTextInput() {
		textInput = new JTextField();
		textInput.setBorder(new MatteBorder(1, 1, 1, 1, (Color) new Color(0, 0, 0)));

		frmQuicktodoBeta.getContentPane().add(textInput, "cell 0 0,growx");
		textInput.setColumns(10);
	}

	private void initResultTextPanel() {
		resultTextPanel = new JTextPane();
		resultTextPanel.setBackground(RESULTTEXTPANEL_BG_COLOR);
		resultTextPanel.setBorder(null);
		resultTextPanel.setContentType("text/html");
		resultTextPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		resultTextPanel.setFont(new Font("Calibri", Font.PLAIN, 5));
		resultTextPanel.setEditable(false);
		frmQuicktodoBeta.getContentPane().add(resultTextPanel, "cell 0 1,grow");
	}

	private void initTaskDisplayPane() {
		taskDisplayPane = new JPanel();
		taskDisplayPane.setBackground(PROGRAM_BG_COLOR);
		taskDisplayPane.setFont(new Font("Calibri", Font.PLAIN, 11));
		taskDisplayPane.setBorder(null);
		
		scrollPane.setViewportView(taskDisplayPane);
		taskDisplayPane.setLayout(new BoxLayout(taskDisplayPane, BoxLayout.Y_AXIS));
	}

	private void initScrollPane() {
		scrollPane = new JScrollPane();
		scrollPane.setBackground(new Color(0, 204, 153));
		scrollPane.getVerticalScrollBar().setUnitIncrement(16);
		scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
		scrollPane.setBackground(TASK_COLOR_1);
		
		frmQuicktodoBeta.getContentPane().add(scrollPane, "cell 0 2,grow");
	}
	
	private String getHelpString() {
		try {
			InputStreamReader isr = new InputStreamReader(Gui.class.getResourceAsStream("/resource/"+Constants.DEFAULT_HELP_FILE_LOC));
			
			/* 
			//Use this guy for debugging
			BufferedReader br = new BufferedReader(
					new FileReader("src\\resource\\"+Constants.DEFAULT_HELP_FILE_LOC));
			 */
			
			BufferedReader br = new BufferedReader(isr);
			
			String help = "" ;
			String line;
			while((line = br.readLine()) != null) {
				help += line;
			}
			
			br.close();
			
			return help;
		} catch (FileNotFoundException e) {
			return "Help file not found.";
		} catch (IOException e) {
			return "Error while reading Help file.";
		}
	}
	
	private void exit() {
		System.exit(0);
	}
}
